module SM4_Core (
    input  wire clk_i,
    input  wire rst_n_i,

    input  wire [1:0] mode_i,  //00:密钥拓展 11：加密 01：解密
    input  wire MK_Valid_i,
    input  wire [127:0] Key_i,
    input  wire Data_Valid_i,
    input  wire [127:0] Data_i,

    output wire RK_Ready_o,
    output wire Data_Ready_o,
    output wire [127:0] Data_o
);

//constant---------------------------------------------------------------- 

wire [31:0] CK[0:31] ;
assign CK[0] = 32'h00070E15, CK[1] = 32'h1C232A31, CK[2] = 32'h383F464D, CK[3] = 32'h545B6269;
assign CK[4] = 32'h70777E85, CK[5] = 32'h8C939AA1, CK[6] = 32'hA8AFB6BD, CK[7] = 32'hC4CBD2D9;
assign CK[8] = 32'hE0E7EEF5, CK[9] = 32'hFC030A11, CK[10] = 32'h181F262D, CK[11] = 32'h343B4249;
assign CK[12] = 32'h50575E65, CK[13] = 32'h6C737A81, CK[14] = 32'h888F969D, CK[15] = 32'hA4ABB2B9;
assign CK[16] = 32'hC0C7CED5, CK[17] = 32'hDCE3EAF1, CK[18] = 32'hF8FF060D, CK[19] = 32'h141B2229;
assign CK[20] = 32'h30373E45, CK[21] = 32'h4C535A61, CK[22] = 32'h686F767D, CK[23] = 32'h848B9299;
assign CK[24] = 32'hA0A7AEB5, CK[25] = 32'hBCC3CAD1, CK[26] = 32'hD8DFE6ED, CK[27] = 32'hF4FB0209;
assign CK[28] = 32'h10171E25, CK[29] = 32'h2C333A41, CK[30] = 32'h484F565D, CK[31] = 32'h646B7279;

wire [31:0] FK[0:3];
assign FK[0] = 32'hA3B1BAC6, FK[1] = 32'h56AA3350, FK[2] = 32'h677D9197, FK[3] = 32'hB27022DC;

//input---------------------------------------------------------------------
//key schedule 
wire [31:0] MK0 ;
wire [31:0] MK1 ;
wire [31:0] MK2 ;
wire [31:0] MK3 ;
assign MK0 = Key_i[127:96];
assign MK1 = Key_i[95:64];
assign MK2 = Key_i[63:32];
assign MK3 = Key_i[31:0];

reg [31:0] K[0:35] ; //RK iter register
always @(*) begin
    K[0] = MK0 ^ FK[0];
    K[1] = MK1 ^ FK[1];
    K[2] = MK2 ^ FK[2];
    K[3] = MK3 ^ FK[3];
end

//data enc/dec
//wire [31:0] RK[0:31];
reg [31:0] X_iter[0:35]; //data iter register
// generate
//     for (genvar i = 0; i<32 ; i=i+1 ) begin
//         assign RK[i] = RoundKey_i[(i+1)*32-1:i*32];
//     end
// endgenerate

always @(*) begin
    X_iter[0] = Data_i[127:96];
    X_iter[1] = Data_i[95:64];
    X_iter[2] = Data_i[63:32];
    X_iter[3] = Data_i[31:0];
end

//round contorl logic-----------------------------------------------------------
//counter
reg [7:0] cnt;
reg Data_Valid_r;
reg MK_Valid_r;
wire start;
wire finished;
wire cnt_inc;
always @(posedge clk_i ) begin
    if(!rst_n_i) begin
        Data_Valid_r <= 0;
        MK_Valid_r <= 0;
    end
    else begin
        Data_Valid_r <= Data_Valid_i;
        MK_Valid_r <= MK_Valid_i;
    end
end
always @(posedge clk_i ) begin
    if(!rst_n_i || start) begin
        cnt <= 0;
    end
    else if(cnt_inc) begin
        cnt <= cnt + 1;
    end
    else if(finished) begin
        cnt <= 8'd32;
    end
end
assign start = (Data_Valid_i & (!Data_Valid_r)) | (MK_Valid_i & (!MK_Valid_r));
assign finished = (cnt == 8'd31);
assign cnt_inc = (Data_Valid_i | MK_Valid_i) & (~finished);

//round function input contorl
reg [31:0] x0_i, x1_i, x2_i, x3_i;
reg [31:0] rk_i;
wire [31:0] x4_o;
wire kgen_en = ~mode_i[0];

always @(*) begin
    case (cnt)
        0 : begin
            x0_i = (!mode_i[0])?  K[0]  : X_iter[0]   ;
            x1_i = (!mode_i[0])?  K[1]  : X_iter[1]   ;
            x2_i = (!mode_i[0])?  K[2]  : X_iter[2]   ;
            x3_i = (!mode_i[0])?  K[3]  : X_iter[3]   ;
            rk_i = (!mode_i[0])?  CK[0] : 
                    (mode_i[1])?  K[4]  : K[35]; 
        end
        1 : begin
            x0_i = (!mode_i[0])? K[1] : X_iter[1];
            x1_i = (!mode_i[0])? K[2] : X_iter[2];
            x2_i = (!mode_i[0])? K[3] : X_iter[3];
            x3_i = (!mode_i[0])? K[4] : X_iter[4];
            rk_i = (!mode_i[0])? CK[1]  :
                    (mode_i[1])? K[5]   : K[34];
        end
        2 : begin
            x0_i = (!mode_i[0])? K[2] : X_iter[2];
            x1_i = (!mode_i[0])? K[3] : X_iter[3];
            x2_i = (!mode_i[0])? K[4] : X_iter[4];
            x3_i = (!mode_i[0])? K[5] : X_iter[5];
            rk_i = (!mode_i[0])? CK[2]  :
                    (mode_i[1])? K[6]   : K[33];
        end
        3 : begin
            x0_i = (!mode_i[0])? K[3] : X_iter[3];
            x1_i = (!mode_i[0])? K[4] : X_iter[4];
            x2_i = (!mode_i[0])? K[5] : X_iter[5];
            x3_i = (!mode_i[0])? K[6] : X_iter[6];
            rk_i = (!mode_i[0])? CK[3]  :
                    (mode_i[1])? K[7]   : K[32];
        end
        4 : begin
            x0_i = (!mode_i[0])? K[4] : X_iter[4];
            x1_i = (!mode_i[0])? K[5] : X_iter[5];
            x2_i = (!mode_i[0])? K[6] : X_iter[6];
            x3_i = (!mode_i[0])? K[7] : X_iter[7];
            rk_i = (!mode_i[0])? CK[4]  :
                    (mode_i[1])? K[8]   : K[31];
        end
        5 : begin
            x0_i = (!mode_i[0])? K[5] : X_iter[5];
            x1_i = (!mode_i[0])? K[6] : X_iter[6];
            x2_i = (!mode_i[0])? K[7] : X_iter[7];
            x3_i = (!mode_i[0])? K[8] : X_iter[8];
            rk_i = (!mode_i[0])? CK[5]  :
                    (mode_i[1])? K[9]   : K[30];
        end
        6 : begin
            x0_i = (!mode_i[0])? K[6] : X_iter[6];
            x1_i = (!mode_i[0])? K[7] : X_iter[7];
            x2_i = (!mode_i[0])? K[8] : X_iter[8];
            x3_i = (!mode_i[0])? K[9] : X_iter[9];
            rk_i = (!mode_i[0])? CK[6]  :
                    (mode_i[1])? K[10]   : K[29];
        end
        7 : begin
            x0_i = (!mode_i[0])? K[7] : X_iter[7];
            x1_i = (!mode_i[0])? K[8] : X_iter[8];
            x2_i = (!mode_i[0])? K[9] : X_iter[9];
            x3_i = (!mode_i[0])? K[10] : X_iter[10];
            rk_i = (!mode_i[0])? CK[7]  :
                    (mode_i[1])? K[11]   : K[28];
        end
        8 : begin
            x0_i = (!mode_i[0])? K[8] : X_iter[8];
            x1_i = (!mode_i[0])? K[9] : X_iter[9];
            x2_i = (!mode_i[0])? K[10] : X_iter[10];
            x3_i = (!mode_i[0])? K[11] : X_iter[11];
            rk_i = (!mode_i[0])? CK[8]  :
                    (mode_i[1])? K[12]   : K[27];
        end
        9 : begin
            x0_i = (!mode_i[0])? K[9] : X_iter[9];
            x1_i = (!mode_i[0])? K[10] : X_iter[10];
            x2_i = (!mode_i[0])? K[11] : X_iter[11];
            x3_i = (!mode_i[0])? K[12] : X_iter[12];
            rk_i = (!mode_i[0])? CK[9]  :
                    (mode_i[1])? K[13]   : K[26];
        end
        10 : begin
            x0_i = (!mode_i[0])? K[10] : X_iter[10];
            x1_i = (!mode_i[0])? K[11] : X_iter[11];
            x2_i = (!mode_i[0])? K[12] : X_iter[12];
            x3_i = (!mode_i[0])? K[13] : X_iter[13];
            rk_i = (!mode_i[0])? CK[10]  :
                    (mode_i[1])? K[14]   : K[25];
        end
        11 : begin
            x0_i = (!mode_i[0])? K[11] : X_iter[11];
            x1_i = (!mode_i[0])? K[12] : X_iter[12];
            x2_i = (!mode_i[0])? K[13] : X_iter[13];
            x3_i = (!mode_i[0])? K[14] : X_iter[14];
            rk_i = (!mode_i[0])? CK[11]  :
                    (mode_i[1])? K[15]   : K[24];
        end
        12 : begin
            x0_i = (!mode_i[0])? K[12] : X_iter[12];
            x1_i = (!mode_i[0])? K[13] : X_iter[13];
            x2_i = (!mode_i[0])? K[14] : X_iter[14];
            x3_i = (!mode_i[0])? K[15] : X_iter[15];
            rk_i = (!mode_i[0])? CK[12]  :
                    (mode_i[1])? K[16]   : K[23];
        end
        13 : begin
            x0_i = (!mode_i[0])? K[13] : X_iter[13];
            x1_i = (!mode_i[0])? K[14] : X_iter[14];
            x2_i = (!mode_i[0])? K[15] : X_iter[15];
            x3_i = (!mode_i[0])? K[16] : X_iter[16];
            rk_i = (!mode_i[0])? CK[13]  :
                    (mode_i[1])? K[17]   : K[22];
        end
        14 : begin
            x0_i = (!mode_i[0])? K[14] : X_iter[14];
            x1_i = (!mode_i[0])? K[15] : X_iter[15];
            x2_i = (!mode_i[0])? K[16] : X_iter[16];
            x3_i = (!mode_i[0])? K[17] : X_iter[17];
            rk_i = (!mode_i[0])? CK[14]  :
                    (mode_i[1])? K[18]   : K[21];
        end
        15 : begin
            x0_i = (!mode_i[0])? K[15] : X_iter[15];
            x1_i = (!mode_i[0])? K[16] : X_iter[16];
            x2_i = (!mode_i[0])? K[17] : X_iter[17];
            x3_i = (!mode_i[0])? K[18] : X_iter[18];
            rk_i = (!mode_i[0])? CK[15]  :
                    (mode_i[1])? K[19]   : K[20];
        end
        16 : begin
            x0_i = (!mode_i[0])? K[16] : X_iter[16];
            x1_i = (!mode_i[0])? K[17] : X_iter[17];
            x2_i = (!mode_i[0])? K[18] : X_iter[18];
            x3_i = (!mode_i[0])? K[19] : X_iter[19];
            rk_i = (!mode_i[0])? CK[16]  :
                    (mode_i[1])? K[20]   : K[19];
        end
        17 : begin
            x0_i = (!mode_i[0])? K[17] : X_iter[17];
            x1_i = (!mode_i[0])? K[18] : X_iter[18];
            x2_i = (!mode_i[0])? K[19] : X_iter[19];
            x3_i = (!mode_i[0])? K[20] : X_iter[20];
            rk_i = (!mode_i[0])? CK[17]  :
                    (mode_i[1])? K[21]   : K[18];
        end
        18 : begin
            x0_i = (!mode_i[0])? K[18] : X_iter[18];
            x1_i = (!mode_i[0])? K[19] : X_iter[19];
            x2_i = (!mode_i[0])? K[20] : X_iter[20];
            x3_i = (!mode_i[0])? K[21] : X_iter[21];
            rk_i = (!mode_i[0])? CK[18]  :
                    (mode_i[1])? K[22]   : K[17];
        end
        19 : begin
            x0_i = (!mode_i[0])? K[19] : X_iter[19];
            x1_i = (!mode_i[0])? K[20] : X_iter[20];
            x2_i = (!mode_i[0])? K[21] : X_iter[21];
            x3_i = (!mode_i[0])? K[22] : X_iter[22];
            rk_i = (!mode_i[0])? CK[19]  :
                    (mode_i[1])? K[23]   : K[16];
        end
        20 : begin
            x0_i = (!mode_i[0])? K[20] : X_iter[20];
            x1_i = (!mode_i[0])? K[21] : X_iter[21];
            x2_i = (!mode_i[0])? K[22] : X_iter[22];
            x3_i = (!mode_i[0])? K[23] : X_iter[23];
            rk_i = (!mode_i[0])? CK[20]  :
                    (mode_i[1])? K[24]   : K[15];
        end
        21 : begin
            x0_i = (!mode_i[0])? K[21] : X_iter[21];
            x1_i = (!mode_i[0])? K[22] : X_iter[22];
            x2_i = (!mode_i[0])? K[23] : X_iter[23];
            x3_i = (!mode_i[0])? K[24] : X_iter[24];
            rk_i = (!mode_i[0])? CK[21]  :
                    (mode_i[1])? K[25]   : K[14];
        end
        22 : begin
            x0_i = (!mode_i[0])? K[22] : X_iter[22];
            x1_i = (!mode_i[0])? K[23] : X_iter[23];
            x2_i = (!mode_i[0])? K[24] : X_iter[24];
            x3_i = (!mode_i[0])? K[25] : X_iter[25];
            rk_i = (!mode_i[0])? CK[22]  :
                    (mode_i[1])? K[26]   : K[13];
        end
        23 : begin
            x0_i = (!mode_i[0])? K[23] : X_iter[23];
            x1_i = (!mode_i[0])? K[24] : X_iter[24];
            x2_i = (!mode_i[0])? K[25] : X_iter[25];
            x3_i = (!mode_i[0])? K[26] : X_iter[26];
            rk_i = (!mode_i[0])? CK[23]  :
                    (mode_i[1])? K[27]   : K[12];
        end
        24 : begin
            x0_i = (!mode_i[0])? K[24] : X_iter[24];
            x1_i = (!mode_i[0])? K[25] : X_iter[25];
            x2_i = (!mode_i[0])? K[26] : X_iter[26];
            x3_i = (!mode_i[0])? K[27] : X_iter[27];
            rk_i = (!mode_i[0])? CK[24]  :
                    (mode_i[1])? K[28]   : K[11];
        end
        25 : begin
            x0_i = (!mode_i[0])? K[25] : X_iter[25];
            x1_i = (!mode_i[0])? K[26] : X_iter[26];
            x2_i = (!mode_i[0])? K[27] : X_iter[27];
            x3_i = (!mode_i[0])? K[28] : X_iter[28];
            rk_i = (!mode_i[0])? CK[25]  :
                    (mode_i[1])? K[29]   : K[10];
        end
        26 : begin
            x0_i = (!mode_i[0])? K[26] : X_iter[26];
            x1_i = (!mode_i[0])? K[27] : X_iter[27];
            x2_i = (!mode_i[0])? K[28] : X_iter[28];
            x3_i = (!mode_i[0])? K[29] : X_iter[29];
            rk_i = (!mode_i[0])? CK[26]  :
                    (mode_i[1])? K[30]   : K[9];
        end
        27 : begin
            x0_i = (!mode_i[0])? K[27] : X_iter[27];
            x1_i = (!mode_i[0])? K[28] : X_iter[28];
            x2_i = (!mode_i[0])? K[29] : X_iter[29];
            x3_i = (!mode_i[0])? K[30] : X_iter[30];
            rk_i = (!mode_i[0])? CK[27]  :
                    (mode_i[1])? K[31]   : K[8];
        end
        28 : begin
            x0_i = (!mode_i[0])? K[28] : X_iter[28];
            x1_i = (!mode_i[0])? K[29] : X_iter[29];
            x2_i = (!mode_i[0])? K[30] : X_iter[30];
            x3_i = (!mode_i[0])? K[31] : X_iter[31];
            rk_i = (!mode_i[0])? CK[28]  :
                    (mode_i[1])? K[32]   : K[7];
        end
        29 : begin
            x0_i = (!mode_i[0])? K[29] : X_iter[29];
            x1_i = (!mode_i[0])? K[30] : X_iter[30];
            x2_i = (!mode_i[0])? K[31] : X_iter[31];
            x3_i = (!mode_i[0])? K[32] : X_iter[32];
            rk_i = (!mode_i[0])? CK[29]  :
                    (mode_i[1])? K[33]   : K[6];
        end
        30 : begin
            x0_i = (!mode_i[0])? K[30] : X_iter[30];
            x1_i = (!mode_i[0])? K[31] : X_iter[31];
            x2_i = (!mode_i[0])? K[32] : X_iter[32];
            x3_i = (!mode_i[0])? K[33] : X_iter[33];
            rk_i = (!mode_i[0])? CK[30]  :
                    (mode_i[1])? K[34]   : K[5];
        end
        31 : begin
            x0_i = (!mode_i[0])? K[31] : X_iter[31];
            x1_i = (!mode_i[0])? K[32] : X_iter[32];
            x2_i = (!mode_i[0])? K[33] : X_iter[33];
            x3_i = (!mode_i[0])? K[34] : X_iter[34];
            rk_i = (!mode_i[0])? CK[31]  :
                    (mode_i[1])? K[35]   : K[4];
        end
        default : begin
            x0_i = 32'b0;
            x1_i = 32'b0;
            x2_i = 32'b0;
            x3_i = 32'b0;
            rk_i = 32'b0;
        end
            
    endcase
end

Round_Fuction RF0(
     .kgen_en(kgen_en),     //1: 生成轮密钥 0： 生成加解密数据    
     .x0_i(x0_i),
     .x1_i(x1_i),
     .x2_i(x2_i),
     .x3_i(x3_i),
     .rk_i(rk_i),        
     .x4_o(x4_o)
);

//round result DFF
generate
    for (genvar i = 0; i < 32 ; i = i+1 ) begin
        always @(posedge clk_i ) begin
            if(!rst_n_i) begin
                X_iter[i+4] <= 32'b0;
            end
            else if(cnt == i && mode_i[0]==1'b1) begin
                X_iter[i+4] <= x4_o;                
            end
        end        
    end
endgenerate

generate
    for (genvar i= 0;i<32 ;i=i+1 ) begin
        always @(posedge clk_i ) begin
            if(!rst_n_i)
                K[i+4] <= 32'b0;
            else if (cnt == i && mode_i[0]==1'b0) begin
                K[i+4] <= x4_o;
            end
        end
    end
endgenerate


//output-------------------------------------------------------------------
assign RK_Ready_o = (cnt == 8'd32) && (mode_i[0] == 1'b0);
assign Data_Ready_o = (cnt == 8'd32) && (mode_i[0] == 1'b1);
assign Data_o = {X_iter[35],X_iter[34],X_iter[33],X_iter[32]};



endmodule //SM4_Core